package microcredit.gui.util;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.util.List;

import javax.swing.ComboBoxEditor;
import javax.swing.DefaultListModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.border.Border;
import javax.swing.text.JTextComponent;

import net.java.balloontip.BalloonTip;

import org.jdesktop.swingx.JXDatePicker;

public class UIUtil {

	public  static <E> Component createListMessage(String message, List<E> data, String suffix) {
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		
		DefaultListModel listModel = new DefaultListModel();
		JList list = new JList(listModel);
		JScrollPane scrollPane = new JScrollPane(list);
		scrollPane.setPreferredSize(new Dimension(400, 250));
		
		for (E e2 : data) {
			listModel.addElement(e2);
		}
		panel.add(new JLabel(message), BorderLayout.NORTH);
		panel.add(scrollPane, BorderLayout.CENTER);
		if (suffix != null) {
			panel.add(new JLabel(suffix), BorderLayout.SOUTH);
		}
		return panel;
		
	}
	
	public  static <E> Component createListMessage(String message, List<E> data) {
		return createListMessage(message, data, null);
	}
	
	public static void setMandatoryFields(Component root, String...fieldNames) {
		for (String name : fieldNames) {
			Component c = findComponent(root, name, true);
			if (c instanceof JComponent) {
				
				boolean isCombo = c instanceof JComboBox;
				
				c = resolveComponent((JComponent)c);
				if (isCombo) {
					((JComponent) c).setOpaque(true);
				}
				c.setBackground(Color.decode("#e5fed2"));
			}
		}
	}
	
	public static boolean applyNotification(Component root, final String componentName, String message) {
		Component c = findComponent(root, componentName, true);
		
		if (c instanceof JComponent) {
			applyValidation(message, resolveComponent(((JComponent)c)), ((JComponent)c)); 
			return true;
		}
		return false;
	}

	private static void applyValidation(final String message, final JComponent comp, JComponent originalComponent) {
		comp.putClientProperty("$$notification", Boolean.TRUE);
		comp.putClientProperty("$$notification_border", comp.getBorder());
		comp.putClientProperty("$$notification_background", comp.getBackground());
		comp.putClientProperty("$$notification_original", originalComponent);
		
//			comp.setBorder(new MatteBorder(1, 1, 1, 1, Color.RED));
		comp.setBackground(Color.decode("#f9c4aa"));
		
		if (originalComponent instanceof JComboBox) {
			if (((JComboBox) originalComponent).isEditable()) {
				((JComboBox) originalComponent).setPopupVisible(false);
			}
			originalComponent.setBackground(Color.decode("#f9c4aa"));
		}
		
		switchTab(comp);
		
		EventQueue.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				
				Toolkit.getDefaultToolkit().beep();
				
				comp.requestFocus();
				if (comp instanceof JTextComponent) {
					((JTextComponent) comp).selectAll();
				}
				
				BalloonTip tip = (BalloonTip)comp.getClientProperty("$$balloon");
				if (tip != null) {
					tip.setVisible(false);
					comp.putClientProperty("$$balloon", null);
				}
				
				
				String m = "<html><font color='red'>"+message+"</font>";
				tip = new BalloonTip(comp, m);
				tip.setVisible(true);
				tip.refreshLocation();
				comp.putClientProperty("$$balloon", tip);
				
//				
//				
//				Action hideaction = comp.getActionMap().get("hideTip");
//				Action action = comp.getActionMap().get("postTip");
//				//it is also possible to use own Timer to display 
//				//ToolTip with custom delay, but here we just 
//				//display it immediately
//				if (action != null) {
//					hideaction.actionPerformed(new ActionEvent(comp, ActionEvent.ACTION_PERFORMED, "hideTip"));
//				    action.actionPerformed(new ActionEvent(comp, ActionEvent.ACTION_PERFORMED, "postTip"));
//				}
			}
		});

	}

	private static void switchTab(Component c) {
		Component parent =c;
		Component previousParent = c;
		while ((parent = parent.getParent()) != null) {
			if (parent instanceof JTabbedPane) {
				((JTabbedPane) parent).setSelectedComponent(previousParent);
				((JTabbedPane) parent).revalidate();
				((JTabbedPane) parent).repaint();
			}
			previousParent = parent;
		}
		
	}
	
	private static Component findComponent(Component root, final String name, final boolean checkForVisibility) {
		Component c = iterate(root, new ComponentIterator<Component>() {
			@Override
			public boolean iterateChildren(Component c) {
				return true;
			}
			@Override
			public Component iterate(Component c) {
				if (c.getName() != null && c.getName().equals(name)
						&& (!checkForVisibility || c.isVisible())) {
					return c;
				}
				return null;
			}
		});
		return c;
	}
	
	private static JComponent resolveComponent(JComponent c) {
		if (c instanceof JPanel) {
			if (c.getComponentCount()> 0) {
				return resolveComponent((JComponent)c.getComponents()[0]);
			}
		} else if (c instanceof JXDatePicker) {
			c = ((JXDatePicker) c).getEditor();
		} else if (c instanceof JComboBox) {
			ComboBoxEditor editor = ((JComboBox) c).getEditor();
			return (JComponent)editor.getEditorComponent();
		} 
		if (c instanceof JSpinner) {
			return ((JSpinner) c).getEditor();
		}
		return c;
	}
	
	public static void clearValidations(Component root) {
		iterate(root, new ComponentIterator<Void>() {
			@Override
			public boolean iterateChildren(Component c) {
				return true;
			}
			@Override
			public Void iterate(Component c) {
				if (c instanceof JComponent) {
					clearValidation( ((JComponent)c));
				}
				return null;
			}
			
		});
	}
	
	private static void clearValidation(JComponent comp) {
		Boolean notification = (Boolean)comp.getClientProperty("$$notification");
		if (notification != null && notification.booleanValue()) {
			BalloonTip tip = (BalloonTip)comp.getClientProperty("$$balloon");
			if (tip != null) {
				tip.setVisible(false);
				comp.putClientProperty("$$balloon", null);
			}
			
			JComponent original = (JComponent)comp.getClientProperty("$$notification_original");
			if (original instanceof JComboBox) {
				((JComboBox) original).setPopupVisible(false);
			}
			
			comp.setBorder((Border)comp.getClientProperty("$$notification_border"));
			comp.setBackground((Color)comp.getClientProperty("$$notification_background"));
			comp.putClientProperty("$$notification", null);
			comp.putClientProperty("$$notification_border", null);
			comp.putClientProperty("$$notification_background", null);
			comp.putClientProperty("$$notification_original", null);
		}
	}
	

    public static <E> E iterate(Component c, ComponentIterator<E> iterator) {
    	if (c == null) {
    		return null;
    	}
    	E retVal = null;
    	retVal = iterator.iterate(c);
    	
    	if (retVal != null) {
    		return retVal;
    	}
    	
    	if (!iterator.iterateChildren(c)) {
    		return null;
    	}
    	
    	if (c instanceof JMenu) {
			retVal = iterateChildren(c, ((JMenu) c).getMenuComponents(),iterator);
			if (retVal != null) {
				return retVal;
			}
		}
		if (c instanceof Container) {
			retVal = iterateChildren(c, ((Container) c).getComponents(),iterator);
		}
		return retVal;
    }
    
    private static<E> E iterateChildren(Component parent, Component[] components, ComponentIterator<E> iterator) {
    	for (int i = 0; i < components.length; i++) {
			Component component = components[i];
			E returnValue = iterate(component, iterator);
			if (returnValue != null) {
				return returnValue;
			}
		}
    	return null;
    }
	
	
}
